home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1999 July: Technology Seed / ADC Seed CD - July 1999.toast / USB / Mac OS USB DDK v1.2 / Examples / USBSampleStorageDriver / StorageClassShim.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-04-15  |  10.4 KB  |  382 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        StorageClassShim.c
  3.  
  4.     Contains:    All functions for the Sample USB Storage Class Shim
  5.  
  6.     Version:    1.1
  7.  
  8.     Copyright:    © 1998-1999 by Apple Computer, Inc., all rights reserved.
  9.  
  10. */
  11.  
  12. /*
  13. MacOS Headers
  14. */
  15. #include <CodeFragments.h>
  16. #include <Devices.h>
  17. #include <DriverServices.h>
  18. #include <MacTypes.h>
  19. #include <Processes.h>
  20. #include <Resources.h>
  21. #include <USB.h>
  22.  
  23.  
  24. /*
  25. Driver Headers
  26. */
  27. #include "StorageClassShim.h"
  28. #include "SampleStorageVersion.h"
  29. #include "SampleStorageDeviceID.h"
  30.  
  31.  
  32. /*
  33. Definitions, Enumerations and Typedefs used by the Shim
  34. */
  35. // Enumerations for the range of Unittable entries
  36. // in which InstallDriverFromMemory will try to install the driver
  37. enum
  38. {
  39.     kUnitTableEntryStart    = 48,
  40.     kUnitTableEntryEnd        = 127
  41. };
  42.  
  43. // These are the structures and definitions used for the Refnum Association array.
  44. // This array handles associating USBDeviceRefs with UnitTable Driver refnums, so
  45. // Drivers can be removed from the UnitTable when the drive is removed.
  46. enum
  47. {
  48.     kMaxNumberOfDrives = 50
  49. };
  50.  
  51. struct RefnumAssociation
  52. {
  53.     USBDeviceRef     theDevRef;
  54.     DriverRefNum    drvrRefNum;
  55. };
  56.  
  57. typedef struct RefnumAssociation    RefnumAssociation;
  58.  
  59.  
  60. /*
  61. Global variables used by the Shim
  62. */
  63. static RefnumAssociation    theRefAssoc[kMaxNumberOfDrives];
  64. static Boolean                 shimInFile;
  65. static FSSpec                shimFSSpec;
  66. static TimerID                gGiveTimeTimer = nil;
  67. static DriverRefNum            gNewDrvrRef = 0;
  68. static USBDeviceNotificationParameterBlock     gpb;
  69.  
  70.  
  71. /* 
  72. Prototypes for internal support functions
  73. */
  74. static OSStatus     GiveTimeSecondaryInterrupt( void *p1, void *p2);
  75. static OSStatus     ShimOpenDriver(USBDeviceRef theDevRef);
  76. static OSStatus     ShimCloseDriver(USBDeviceRef theDevRef);
  77.  
  78.  
  79. /*
  80. Exported Functions for the Code Fragment Manager
  81. */
  82. // This routine gets called by the Code Fragment Manager when the shim's code fragment is loaded
  83. OSErr CFragInitRoutine(CFragInitBlockPtr initBlkPtr)
  84. {
  85.     shimInFile = false;
  86.     
  87.     if (CFragHasFileLocation(initBlkPtr->fragLocator.where))
  88.     {
  89.         shimInFile = true;
  90.         shimFSSpec = *(initBlkPtr->fragLocator.u.onDisk.fileSpec);            // save the FSSpec, in case we need it later
  91.     }
  92.     return noErr;
  93. }
  94.  
  95.  
  96. /*
  97. Functions Exported for the USB Expert
  98. */
  99. // This routine is exported for use by the USB Expert to initialize the shim when
  100. // the expert loads it
  101. OSStatus USBShim(void)
  102. {
  103.     OSStatus status = noErr;
  104.  
  105.     // Clear out the Association array
  106.     BlockZero((Ptr) theRefAssoc, sizeof(RefnumAssociation) * kMaxNumberOfDrives);
  107.  
  108.     // Setup notification for Vendor Specific Devices
  109.     gpb.usbDeviceNotification     = kNotifyAnyEvent;            // tell me about everything
  110.     gpb.usbClass                 = kUSBAnyClass;
  111.     gpb.usbSubClass             = kUSBAnySubClass;
  112.     gpb.usbProtocol             = kUSBAnyProtocol;
  113.     gpb.usbVendor                 = kDriverVendorID;            // Notify me of my specific Vendor ID
  114.     gpb.usbProduct                 = kDriverProductID;            // Notify me of my specific Product ID
  115.     gpb.result = noErr;
  116.     gpb.callback = (USBDeviceNotificationCallbackProcPtr) &myNotificationCallback;        
  117.     gpb.refcon = nil;
  118.      USBInstallDeviceNotification (&gpb);    
  119.  
  120.     return status;
  121. }
  122.  
  123. // This routine is exported for use by the USB Expert to terminate the shim before it gets removed
  124. void USBShimTermination(void)
  125. {
  126.     // Remove the device notofication
  127.     USBRemoveDeviceNotification(gpb.token); 
  128. }
  129.  
  130. // This function is passed to the USB Expert for device add/remove notification
  131. void myNotificationCallback(USBDeviceNotificationParameterBlock *pb)
  132. {
  133.     switch(pb->usbDeviceNotification)        // why were we notified?
  134.     {
  135.         case kNotifyAddDevice:                // because mass storage device appeared
  136.         case kNotifyAddInterface:            // because mass storage interface appeared
  137.         {    
  138.             ShimOpenDriver(pb->usbDeviceRef); 
  139.         }
  140.         break;
  141.             
  142.         case kNotifyRemoveDevice:            // because a mass storage device or interface disappeared
  143.         case kNotifyRemoveInterface:        // because a mass storage device or interface disappeared
  144.         {
  145.             ShimCloseDriver(pb->usbDeviceRef);
  146.         }
  147.         break;
  148.             
  149.         default:
  150.             break;
  151.     }
  152. }
  153.  
  154. /*
  155. Functions Internal to the Shim
  156. */
  157. // Function that will load and install the driver for a new device.
  158. // As stated in the USB DDK API, the notification always occurs at task time.
  159. OSStatus ShimOpenDriver(USBDeviceRef theDevRef)
  160. {
  161.     OSStatus            theErr = noErr;
  162.     Handle                hDrvrResource;
  163.     short                loopCount;
  164.     THz                    currentZone;
  165.     
  166.     // We have been notified of a new device, check to see if we already 
  167.     // have installed a driver the USB Device Ref.
  168.     for( loopCount = 0; loopCount < kMaxNumberOfDrives; loopCount++)
  169.     {
  170.         if(theDevRef == theRefAssoc[loopCount].theDevRef)
  171.         {
  172.             // Driver has already been installed for this USBDeviceRef,
  173.             // Nothing else needs to be done, report back noErr to the notifier
  174.             return noErr;
  175.         }
  176.     }
  177.  
  178.     // No driver has been loaded for this USBDeviceRef, find the driver's 'ndrv' resource        
  179.     if (shimInFile)
  180.     {
  181.         short    oldResFileID = 0, 
  182.                 myResFileID = 0;
  183.  
  184.         SetResLoad(true);
  185.         oldResFileID = CurResFile();                                    // get the current resource file ID
  186.         myResFileID = FSpOpenResFile(&shimFSSpec, fsRdPerm);
  187.         UseResFile(myResFileID);                                        // point at the shim file
  188.  
  189.         currentZone = GetZone ();
  190.         SetZone ( SystemZone() );
  191.         hDrvrResource = Get1Resource('ndrv', 128);                        // read in the driver from a ndrv resource
  192.         DetachResource(hDrvrResource);                                    // Detach the resource so it hangs around in the system heap
  193.         SetZone (currentZone);
  194.  
  195.         UseResFile(oldResFileID);                                        // point at the original file
  196.         CloseResFile(myResFileID);                                        // Make sure the resource file is closed
  197.     }
  198.     else
  199.     {
  200.         hDrvrResource = Get1Resource('ndrv', 128);                        // read in the driver from a ndrv resource
  201.     }
  202.     
  203.     // We have found the driver's 'ndrv' resource, install it into the UnitTable
  204.     if (hDrvrResource)
  205.     {
  206.         long                drvrSize;
  207.         Ptr                    pDrvrInMemory;
  208.         DriverRefNum        drvrRefNum;
  209.         void                *pTheStorageClassDispatchTable;
  210.         CFragSymbolClass    symClass;
  211.         CFragConnectionID    connID;
  212.         short                loopCount;
  213.  
  214.         // Lock the Driver Resource in memory
  215.         HLock(hDrvrResource);
  216.  
  217.         // Get the resource information needed to install the Driver
  218.         pDrvrInMemory = *hDrvrResource;
  219.         drvrSize = GetHandleSize(hDrvrResource);
  220.  
  221.         // Install the driver
  222.         theErr = InstallDriverFromMemory(pDrvrInMemory, drvrSize, nil, (RegEntryIDPtr) nil, kUnitTableEntryStart, kUnitTableEntryEnd, &drvrRefNum);
  223.         if ( theErr != noErr )
  224.         {
  225.             // The driver could not be loaded, the shim will return the error and abort the driver load
  226.             return theErr;
  227.         }
  228.  
  229.         gNewDrvrRef = drvrRefNum;
  230.         
  231.         // Save the Driver refnum to remove the driver when the remove notification is recieved
  232.         for( loopCount = 0; loopCount < kMaxNumberOfDrives; loopCount++ )
  233.         {
  234.             if (( theRefAssoc[loopCount].theDevRef == 0 ) && (theRefAssoc[loopCount].drvrRefNum == 0) )
  235.             {
  236.                 theRefAssoc[loopCount].theDevRef = theDevRef;
  237.                 theRefAssoc[loopCount].drvrRefNum = drvrRefNum;
  238.                 break;
  239.             }
  240.         }
  241.  
  242.         // If the array is already full, this drive will not be added to the array.  This means that the Shim will never
  243.         // remove the driver it the drive is detached.  This will only happen if more than kMaxNumberOfDrives are attached
  244.         // at any one time.
  245.  
  246.         // Get the Storage class dispatch table 
  247.         USBGetDriverConnectionID(&theDevRef, &connID);
  248.         currentZone = GetZone ();
  249.         SetZone ( SystemZone() );
  250.         
  251.         theErr = FindSymbol(connID, "\pTheStorageClassDispatchTable", (Ptr *)&pTheStorageClassDispatchTable, &symClass);
  252.         SetZone (currentZone);
  253.  
  254.         // If no error occured, pass the dispatch table pointer to the Driver
  255.         if (theErr == noErr)            
  256.         {
  257.             // Since we know that the Unit Table driver doesn't make any system calls
  258.             // for this control code, we can make the call synchronously.
  259.             theErr = Control(drvrRefNum, 500, &pTheStorageClassDispatchTable);
  260.         }
  261.         
  262.         if ( theErr != noErr )
  263.         {
  264.             // if an error occurs, we should remove the driver from the unittable
  265.             ShimCloseDriver(theDevRef);
  266.         }
  267.     }
  268.     
  269.     return theErr;
  270. }
  271.  
  272. OSStatus ShimCloseDriver(USBDeviceRef theDevRef)
  273. {
  274.     OSStatus            theErr = noErr;
  275.     short                loopCount;
  276.  
  277.     for( loopCount = 0; loopCount < kMaxNumberOfDrives; loopCount++)
  278.     {
  279.         if(theDevRef == theRefAssoc[loopCount].theDevRef)
  280.         {
  281.             if(theRefAssoc[loopCount].drvrRefNum != 0)
  282.             {
  283.                 VCBPtr vol;
  284.                 
  285.                 vol = (VCBPtr) (GetVCBQHdr())->qHead;
  286.                 
  287.                 while (vol)
  288.                 {
  289.                     // Check to see if this volume belongs to the driver
  290.                     // that is being removed
  291.                     if( vol->vcbDRefNum == theRefAssoc[loopCount].drvrRefNum)
  292.                     {
  293.                         IOParam     offlinePB;
  294.                         OSErr        err;
  295.                         
  296.                         BlockZero(&offlinePB, sizeof(IOParam));
  297.                         offlinePB.ioCompletion = nil;    
  298.                         offlinePB.ioResult = noErr;    
  299.                         offlinePB.ioNamePtr = nil;    
  300.                         offlinePB.ioVRefNum = vol->vcbVRefNum;    
  301.                         
  302.                         err = PBUnmountVol((ParamBlockRec *) &offlinePB);
  303.                         if(err == fBsyErr)
  304.                         {
  305.                             err = PBEject((ParamBlockRec *) &offlinePB);
  306.                             gNewDrvrRef = 0;
  307.                         }
  308.                     }
  309.                     
  310.                     vol = (VCBPtr) vol->qLink;
  311.                 }
  312.                 
  313.                 theErr = RemoveDriver(theRefAssoc[loopCount].drvrRefNum, false);
  314.                 theRefAssoc[loopCount].theDevRef = 0;
  315.                 theRefAssoc[loopCount].drvrRefNum = 0;
  316.                 
  317.                 if(gNewDrvrRef == 0)
  318.                 {
  319.                     OSStatus             status;
  320.                     AbsoluteTime        theWait;
  321.                 
  322.                     theWait = DurationToAbsolute(durationSecond);
  323.                     theWait = AddAbsoluteToAbsolute(UpTime(), theWait);
  324.                     status = SetInterruptTimer( &theWait, &GiveTimeSecondaryInterrupt, nil, &gGiveTimeTimer);
  325.                 }
  326.             }
  327.             
  328.             return theErr;
  329.         }
  330.     }
  331.     
  332.     return theErr;
  333. }
  334.  
  335. // This is the interrupt timer to provide the USB Manager to get time if a device was
  336. // removed with a volume in use.
  337. // The shortcomings of this implementation are:
  338. //         1. This will only work if a single in use device is removed.  Multiple devices can cause
  339. //            problems where the shim could get about confused whether the device was reattached.
  340. //        2. This will only work if the device that is removed has only one busy volume mounted.
  341. //            More than one volume may cause the shim to get confused about whether the volume was remounted
  342. OSStatus GiveTimeSecondaryInterrupt( void *p1, void *p2)
  343. {
  344. #pragma unused ( p1, p2 )
  345.     Boolean     DrvrVolumeFound = false;
  346.  
  347.     gGiveTimeTimer = nil;
  348.  
  349.     if(gNewDrvrRef != 0)
  350.     {
  351.         VCBPtr vol;
  352.         
  353.         vol = (VCBPtr) (GetVCBQHdr())->qHead;
  354.         
  355.         while (vol)
  356.         {
  357.             // Check to see if this volume belongs to the driver
  358.             // that is being removed
  359.             if( vol->vcbDRefNum == gNewDrvrRef)
  360.             {
  361.                 DrvrVolumeFound = true;
  362.                 break;
  363.             }
  364.             
  365.             vol = (VCBPtr) vol->qLink;
  366.         }
  367.     }
  368.  
  369.     if( DrvrVolumeFound == false )
  370.     {
  371.         OSStatus             status;
  372.         AbsoluteTime        theWait;
  373.  
  374.         SystemTask();
  375.         theWait = DurationToAbsolute(durationSecond);
  376.         theWait = AddAbsoluteToAbsolute(UpTime(), theWait);
  377.         status = SetInterruptTimer( &theWait, &GiveTimeSecondaryInterrupt, nil, &gGiveTimeTimer);
  378.     }
  379.                 
  380.     return noErr;
  381. }
  382.